package ${basePackage}.core.hutool.bean;

import ${basePackage}.core.hutool.clone.CloneSupport;
import ${basePackage}.core.hutool.util.BeanUtil;
import ${basePackage}.core.hutool.util.ClassUtil;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * 动态Bean，通过反射对Bean的相关方法做操作<br>
 * 支持Map和普通Bean
 *
 * @author Looly
 * @since 3.0.7
 */
public class DynaBean extends CloneSupport implements Serializable {
    private static final long serialVersionUID = 1197818330017827323L;

    private Class<?> beanClass;
    private Object bean;

    /**
     * 创建一个{@link DynaBean}
     *
     * @param bean 普通Bean
     * @return {@link DynaBean}
     */
    public static DynaBean create(Object bean) {
        return new DynaBean(bean);
    }

    /**
     * 创建一个{@link DynaBean}
     *
     * @param beanClass Bean类
     * @param params    构造Bean所需要的参数
     * @return {@link DynaBean}
     */
    public static DynaBean create(Class<?> beanClass, Object... params) {
        return new DynaBean(beanClass, params);
    }

    //------------------------------------------------------------------------ Constructor start

    /**
     * 构造
     *
     * @param beanClass Bean类
     * @param params    构造Bean所需要的参数
     */
    public DynaBean(Class<?> beanClass, Object... params) {
        this(ClassUtil.newInstance(beanClass, params));
    }

    /**
     * 构造
     *
     * @param bean 原始Bean
     */
    public DynaBean(Object bean) {
        //Assert.notNull(bean);
        if (bean instanceof DynaBean) {
            bean = ((DynaBean) bean).getBean();
        }
        this.bean = bean;
        this.beanClass = ClassUtil.getClass(bean);
    }
    //------------------------------------------------------------------------ Constructor end

    /**
     * 获得字段对应值
     *
     * @param <T>       属性值类型
     * @param fieldName 字段名
     * @return 字段值
     * @throws BeanException 反射获取属性值或字段值导致的异常
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String fieldName) throws BeanException {
        if (Map.class.isAssignableFrom(beanClass)) {
            return (T) ((Map<?, ?>) bean).get(fieldName);
        } else {
            try {
                final PropertyDescriptor descriptor = BeanUtil.getPropertyDescriptor(beanClass, fieldName);
                if (null == descriptor) {
                    throw new BeanException("No PropertyDescriptor for {}", fieldName);
                }
                final Method method = descriptor.getReadMethod();
                if (null == method) {
                    throw new BeanException("No get method for {}", fieldName);
                }
                return (T) method.invoke(this.bean);
            } catch (Exception e) {
                throw new BeanException(e);
            }
        }
    }

    /**
     * 获得字段对应值，获取异常返回{@code null}
     *
     * @param <T>       属性值类型
     * @param fieldName 字段名
     * @return 字段值
     * @since 3.1.1
     */
    public <T> T safeGet(String fieldName) {
        try {
            return get(fieldName);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 设置字段值
     *
     * @param fieldName 字段名
     * @param value     字段值
     * @throws BeanException 反射获取属性值或字段值导致的异常
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public void set(String fieldName, Object value) throws BeanException {
        if (Map.class.isAssignableFrom(beanClass)) {
            ((Map) bean).put(fieldName, value);
            return;
        } else {
            try {
                final PropertyDescriptor descriptor = BeanUtil.getPropertyDescriptor(beanClass, fieldName);
                if (null == descriptor) {
                    throw new BeanException("No PropertyDescriptor for {}", fieldName);
                }
                final Method method = descriptor.getWriteMethod();
                if (null == method) {
                    throw new BeanException("No set method for {}", fieldName);
                }
                method.invoke(this.bean, value);
            } catch (Exception e) {
                throw new BeanException(e);
            }
        }
    }

    /**
     * 执行原始Bean中的方法
     *
     * @param methodName 方法名
     * @param params     参数
     * @return 执行结果，可能为null
     */
    public Object invoke(String methodName, Object... params) {
        return ClassUtil.invoke(this.bean, methodName, params);
    }

    /**
     * 获得原始Bean
     *
     * @param <T> Bean类型
     * @return bean
     */
    @SuppressWarnings("unchecked")
    public <T> T getBean() {
        return (T) this.bean;
    }

    /**
     * 获得Bean的类型
     *
     * @param <T> Bean类型
     * @return Bean类型
     */
    @SuppressWarnings("unchecked")
    public <T> Class<T> getBeanClass() {
        return (Class<T>) this.beanClass;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((bean == null) ? 0 : bean.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final DynaBean other = (DynaBean) obj;
        if (bean == null) {
            if (other.bean != null) {
                return false;
            }
        } else if (!bean.equals(other.bean)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return this.bean.toString();
    }
}
